#include "U2HardwarePixelBuffer.h"

#include "U2Image.h"
#include "U2Exception.h"


U2EG_NAMESPACE_USING


//-----------------------------------------------------------------------------    
U2HardwarePixelBuffer::U2HardwarePixelBuffer(size_t width, size_t height, size_t depth,
        PixelFormat format,
        U2HardwareBuffer::Usage usage, bool useSystemMemory, bool useShadowBuffer):
    U2HardwareBuffer(usage, useSystemMemory, useShadowBuffer),
    mWidth(width), mHeight(height), mDepth(depth),
    mFormat(format)
{
    // Default
    mRowPitch = mWidth;
    mSlicePitch = mHeight * mWidth;
	mSizeInBytes = mHeight * mWidth * U2PixelUtil::getNumElemBytes(mFormat);
}

//-----------------------------------------------------------------------------    
U2HardwarePixelBuffer::~U2HardwarePixelBuffer()
{
}

//-----------------------------------------------------------------------------    
void* U2HardwarePixelBuffer::lock(size_t offset, size_t length, LockOptions options)
{
    assert(!isLocked() && "Cannot lock this buffer, it is already locked!");
    assert(offset == 0 && length == mSizeInBytes && "Cannot lock memory region, most lock box or entire buffer");
    
    U2Image::Box myBox(0, 0, 0, mWidth, mHeight, mDepth);
    const U2PixelBox &rv = lock(myBox, options);
    return rv.data;
}

//-----------------------------------------------------------------------------    
const U2PixelBox& U2HardwarePixelBuffer::lock(const U2Image::Box& lockBox, LockOptions options)
{
    if (mUseShadowBuffer)
    {
        if (options != HBL_READ_ONLY)
        {
            // we have to assume a read / write lock so we use the shadow buffer
            // and tag for sync on unlock()
            mShadowUpdated = true;
        }

        mCurrentLock = static_cast<U2HardwarePixelBuffer*>(mpShadowBuffer)->lock(lockBox, options);
    }
    else
    {
        // Lock the real buffer if there is no shadow buffer 
        mCurrentLock = lockImpl(lockBox, options);
        mIsLocked = true;
    }

    return mCurrentLock;
}

//-----------------------------------------------------------------------------    
const U2PixelBox& U2HardwarePixelBuffer::getCurrentLock() 
{ 
    assert(isLocked() && "Cannot get current lock: buffer not locked");
    
    return mCurrentLock; 
}

//-----------------------------------------------------------------------------    
/// Internal implementation of lock()
void* U2HardwarePixelBuffer::lockImpl(size_t offset, size_t length, LockOptions options)
{
    U2_EXCEPT(U2Exception::ERR_INTERNAL_ERROR, "lockImpl(offset,length) is not valid for PixelBuffers and should never be called",
        "HardwarePixelBuffer::lockImpl");
}

//-----------------------------------------------------------------------------    

void U2HardwarePixelBuffer::blit(const U2HardwarePixelBufferSharedPtr &src, const U2Image::Box &srcBox, const U2Image::Box &dstBox)
{
	if(isLocked() || src->isLocked())
	{
		U2_EXCEPT(U2Exception::ERR_INTERNAL_ERROR,
			"Source and destination buffer may not be locked!",
			"HardwarePixelBuffer::blit");
	}
	if(src.getPointer() == this)
	{
		U2_EXCEPT( U2Exception::ERR_INVALIDPARAMS,
            "Source must not be the same object",
            "HardwarePixelBuffer::blit" ) ;
	}
	const U2PixelBox &srclock = src->lock(srcBox, HBL_READ_ONLY);

	LockOptions method = HBL_NORMAL;
	if(dstBox.left == 0 && dstBox.top == 0 && dstBox.front == 0 &&
	   dstBox.right == mWidth && dstBox.bottom == mHeight &&
	   dstBox.back == mDepth)
		// Entire buffer -- we can discard the previous contents
		method = HBL_DISCARD;
		
	const U2PixelBox &dstlock = lock(dstBox, method);
	if(dstlock.getWidth() != srclock.getWidth() ||
    	dstlock.getHeight() != srclock.getHeight() ||
    	dstlock.getDepth() != srclock.getDepth())
	{
		// Scaling desired
		U2Image::scale(srclock, dstlock);
	}
	else
	{
		// No scaling needed
		U2PixelUtil::bulkPixelConversion(srclock, dstlock);
	}

	unlock();
	src->unlock();
}
//-----------------------------------------------------------------------------       
void U2HardwarePixelBuffer::blit(const U2HardwarePixelBufferSharedPtr &src)
{
    blit(src, 
        Box(0,0,0,src->getWidth(),src->getHeight(),src->getDepth()), 
        Box(0,0,0,mWidth,mHeight,mDepth)
    );
}
//-----------------------------------------------------------------------------    
void U2HardwarePixelBuffer::readData(size_t offset, size_t length, void* pDest)
{
	// TODO
	U2_EXCEPT(U2Exception::ERR_NOT_IMPLEMENTED,
			"Reading a byte range is not implemented. Use blitToMemory.",
			"HardwarePixelBuffer::readData");
}
//-----------------------------------------------------------------------------    

void U2HardwarePixelBuffer::writeData(size_t offset, size_t length, const void* pSource,
		bool discardWholeBuffer)
{
	// TODO
	U2_EXCEPT(U2Exception::ERR_NOT_IMPLEMENTED,
			"Writing a byte range is not implemented. Use blitFromMemory.",
			"HardwarePixelBuffer::writeData");
}
//-----------------------------------------------------------------------------    
/**********************@@@@@@@@@@@@@@@@@@@@@
RenderTexture *HardwarePixelBuffer::getRenderTarget(size_t)
{
    U2_EXCEPT(U2Exception::ERR_NOT_IMPLEMENTED,
			"Not yet implemented for this rendersystem.",
			"HardwarePixelBuffer::getRenderTarget");
}
*/
//-----------------------------------------------------------------------------    

void U2HardwarePixelBuffer::_clearSliceRTT(size_t zoffset)
{
}

//-----------------------------------------------------------------------------    

U2HardwarePixelBufferSharedPtr::U2HardwarePixelBufferSharedPtr(U2HardwarePixelBuffer* buf)
    : U2SharedPtr<U2HardwarePixelBuffer>(buf)
{

}   


